package com.sync.platform.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sync.platform.entity.DatabaseConfig;
import com.sync.platform.entity.RestoreTask;
import com.sync.platform.mapper.RestoreTaskMapper;
import com.sync.platform.service.DatabaseConfigService;
import com.sync.platform.service.RestoreTaskService;
import com.sync.platform.util.DatabaseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Restore task service implementation
 */
@Service
public class RestoreTaskServiceImpl extends ServiceImpl<RestoreTaskMapper, RestoreTask> implements RestoreTaskService {

    private static final Logger logger = LoggerFactory.getLogger(RestoreTaskServiceImpl.class);
    
    private static final int EXECUTION_FAILED = 0;
    private static final int EXECUTION_SUCCESS = 1;
    
    private static final int STATUS_DISABLED = 0;
    private static final int STATUS_ENABLED = 1;
    
    @Value("${app.backup-dir:./backups}")
    private String backupDir;
    
    @Autowired
    private DatabaseConfigService databaseConfigService;

    @Override
    public boolean saveRestoreTask(Long id, String name, Long databaseId, String description, MultipartFile backupFile) {
        RestoreTask task = new RestoreTask();
        if (id != null) {
            task = getById(id);
            if (task == null) {
                return false;
            }
        }
        
        task.setName(name);
        task.setDatabaseId(databaseId);
        task.setDescription(description);
        task.setStatus(STATUS_ENABLED);
        
        // Handle file upload if provided
        if (backupFile != null && !backupFile.isEmpty()) {
            try {
                String fileId = uploadBackupFile(backupFile, databaseId);
                String backupFilePath = Paths.get(backupDir, fileId).toString();
                task.setBackupFilePath(backupFilePath);
                task.setOriginalFileName(backupFile.getOriginalFilename());
            } catch (Exception e) {
                logger.error("Error uploading backup file", e);
                return false;
            }
        }
        
        return saveOrUpdate(task);
    }

    @Override
    public Map<String, Object> executeTask(Long id) {
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        
        RestoreTask task = getById(id);
        if (task == null) {
            result.put("message", "恢复任务不存在");
            return result;
        }
        
        // Update execution time
        task.setLastExecutionTime(LocalDateTime.now());
        
        try {
            // Get database configuration
            DatabaseConfig dbConfig = databaseConfigService.getById(task.getDatabaseId());
            if (dbConfig == null) {
                String message = "目标数据库不存在";
                task.setLastExecutionResult(EXECUTION_FAILED);
                task.setLastExecutionMessage(message);
                updateById(task);
                result.put("message", message);
                return result;
            }
            
            // Check if backup file exists
            if (task.getBackupFilePath() == null || task.getBackupFilePath().isEmpty()) {
                String message = "备份文件不存在";
                task.setLastExecutionResult(EXECUTION_FAILED);
                task.setLastExecutionMessage(message);
                updateById(task);
                result.put("message", message);
                return result;
            }
            
            File backupFile = new File(task.getBackupFilePath());
            if (!backupFile.exists() || !backupFile.isFile()) {
                String message = "备份文件不存在或不可访问";
                task.setLastExecutionResult(EXECUTION_FAILED);
                task.setLastExecutionMessage(message);
                updateById(task);
                result.put("message", message);
                return result;
            }
            
            StringBuilder logBuilder = new StringBuilder();
            logBuilder.append("开始恢复数据库: ").append(dbConfig.getName()).append("\n");
            logBuilder.append("使用备份文件: ").append(task.getOriginalFileName()).append("\n");
            
            String dbType = dbConfig.getDbType().toLowerCase();
            boolean restoreSuccess = false;
            
            // Handle different database types
            if ("mysql".equals(dbType)) {
                restoreSuccess = restoreMySql(dbConfig, backupFile, logBuilder);
            } else if ("postgresql".equals(dbType)) {
                restoreSuccess = restorePostgres(dbConfig, backupFile, logBuilder);
            } else {
                // Generic SQL script execution for other database types
                restoreSuccess = restoreGenericSql(dbConfig, backupFile, logBuilder);
            }
            
            // Update task status
            if (restoreSuccess) {
                String message = "数据库恢复成功";
                task.setLastExecutionResult(EXECUTION_SUCCESS);
                task.setLastExecutionMessage(message);
                updateById(task);
                
                result.put("success", true);
                result.put("message", message);
                result.put("log", logBuilder.toString());
            } else {
                String message = "数据库恢复失败，详情请查看日志";
                task.setLastExecutionResult(EXECUTION_FAILED);
                task.setLastExecutionMessage(message);
                updateById(task);
                
                result.put("message", message);
                result.put("log", logBuilder.toString());
            }
            
            return result;
            
        } catch (Exception e) {
            logger.error("Error executing restore task", e);
            
            String message = "恢复执行异常: " + e.getMessage();
            task.setLastExecutionResult(EXECUTION_FAILED);
            task.setLastExecutionMessage(message);
            updateById(task);
            
            result.put("message", message);
            
            return result;
        }
    }

    @Override
    public String uploadBackupFile(MultipartFile file, Long databaseId) {
        try {
            // Create backup directory if it doesn't exist
            Path backupPath = Paths.get(backupDir);
            if (!Files.exists(backupPath)) {
                Files.createDirectories(backupPath);
            }
            
            // Generate a unique filename
            String fileId = UUID.randomUUID().toString() + "_" + databaseId + "_" + 
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            
            // Save the file
            Path filePath = Paths.get(backupDir, fileId);
            Files.copy(file.getInputStream(), filePath);
            
            return fileId;
        } catch (Exception e) {
            logger.error("Error uploading backup file", e);
            throw new RuntimeException("备份文件上传失败: " + e.getMessage(), e);
        }
    }

    @Override
    public List<Map<String, Object>> getBackupHistory(Long databaseId) {
        // Get all restore tasks for the database
        LambdaQueryWrapper<RestoreTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RestoreTask::getDatabaseId, databaseId);
        queryWrapper.orderByDesc(RestoreTask::getCreateTime);
        
        List<RestoreTask> tasks = list(queryWrapper);
        List<Map<String, Object>> historyList = new ArrayList<>();
        
        for (RestoreTask task : tasks) {
            Map<String, Object> entry = new HashMap<>();
            entry.put("id", task.getId());
            entry.put("name", task.getName());
            entry.put("originalFileName", task.getOriginalFileName());
            entry.put("description", task.getDescription());
            entry.put("createTime", task.getCreateTime());
            entry.put("lastExecutionTime", task.getLastExecutionTime());
            entry.put("lastExecutionResult", task.getLastExecutionResult());
            entry.put("lastExecutionMessage", task.getLastExecutionMessage());
            entry.put("status", task.getStatus());
            
            historyList.add(entry);
        }
        
        return historyList;
    }
    
    /**
     * Restore MySQL database
     */
    private boolean restoreMySql(DatabaseConfig dbConfig, File backupFile, StringBuilder logBuilder) {
        try {
            String host = dbConfig.getHost();
            Integer port = dbConfig.getPort();
            String username = dbConfig.getUsername();
            String password = dbConfig.getPassword();
            String dbName = dbConfig.getDatabaseName();
            
            // Build MySQL restore command
            ProcessBuilder processBuilder = new ProcessBuilder(
                "mysql",
                "--host=" + host,
                "--port=" + port,
                "--user=" + username,
                "--password=" + password,
                dbName
            );
            
            Process process = processBuilder.start();
            
            // Pipe the backup file to mysql command
            Files.copy(backupFile.toPath(), process.getOutputStream());
            process.getOutputStream().close();
            
            // Read and log the output
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                logBuilder.append(line).append("\n");
            }
            
            // Read and log any errors
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            boolean hasErrors = false;
            while ((line = errorReader.readLine()) != null) {
                hasErrors = true;
                logBuilder.append("错误: ").append(line).append("\n");
            }
            
            // Wait for process to complete
            int exitCode = process.waitFor();
            logBuilder.append("MySQL恢复进程退出代码: ").append(exitCode).append("\n");
            
            return exitCode == 0 && !hasErrors;
            
        } catch (Exception e) {
            logBuilder.append("MySQL恢复过程中发生异常: ").append(e.getMessage()).append("\n");
            logger.error("Error restoring MySQL database", e);
            return false;
        }
    }
    
    /**
     * Restore PostgreSQL database
     */
    private boolean restorePostgres(DatabaseConfig dbConfig, File backupFile, StringBuilder logBuilder) {
        try {
            String host = dbConfig.getHost();
            Integer port = dbConfig.getPort();
            String username = dbConfig.getUsername();
            String password = dbConfig.getPassword();
            String dbName = dbConfig.getDatabaseName();
            
            // Set PGPASSWORD environment variable
            ProcessBuilder processBuilder = new ProcessBuilder(
                "psql",
                "-h", host,
                "-p", String.valueOf(port),
                "-U", username,
                "-d", dbName,
                "-f", backupFile.getAbsolutePath()
            );
            
            // Set PostgreSQL password environment variable
            Map<String, String> env = processBuilder.environment();
            env.put("PGPASSWORD", password);
            
            Process process = processBuilder.start();
            
            // Read and log the output
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                logBuilder.append(line).append("\n");
            }
            
            // Read and log any errors
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            boolean hasErrors = false;
            while ((line = errorReader.readLine()) != null) {
                // Check if it's a real error or just a notice/warning
                if (line.contains("ERROR:")) {
                    hasErrors = true;
                }
                logBuilder.append(line).append("\n");
            }
            
            // Wait for process to complete
            int exitCode = process.waitFor();
            logBuilder.append("PostgreSQL恢复进程退出代码: ").append(exitCode).append("\n");
            
            return exitCode == 0 && !hasErrors;
            
        } catch (Exception e) {
            logBuilder.append("PostgreSQL恢复过程中发生异常: ").append(e.getMessage()).append("\n");
            logger.error("Error restoring PostgreSQL database", e);
            return false;
        }
    }
    
    /**
     * Restore database using generic SQL script execution
     */
    private boolean restoreGenericSql(DatabaseConfig dbConfig, File backupFile, StringBuilder logBuilder) {
        Connection connection = null;
        try {
            // Establish database connection
            connection = DatabaseUtil.getConnection(
                dbConfig.getUrl(),
                dbConfig.getDriverClass(),
                dbConfig.getUsername(),
                dbConfig.getPassword()
            );
            
            logBuilder.append("成功连接到数据库\n");
            
            // Read SQL script
            StringBuilder sqlScript = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new FileReader(backupFile))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    sqlScript.append(line).append("\n");
                }
            }
            
            // Split the script into individual statements
            String[] statements = sqlScript.toString().split(";");
            logBuilder.append("SQL文件包含 ").append(statements.length).append(" 个语句\n");
            
            // Execute each statement
            Statement stmt = connection.createStatement();
            int successCount = 0;
            for (int i = 0; i < statements.length; i++) {
                String sql = statements[i].trim();
                if (!sql.isEmpty()) {
                    try {
                        stmt.execute(sql);
                        successCount++;
                    } catch (Exception e) {
                        logBuilder.append("执行第 ").append(i + 1).append(" 个SQL语句失败: ").append(e.getMessage()).append("\n");
                    }
                }
            }
            
            logBuilder.append("成功执行 ").append(successCount).append(" 个SQL语句，共 ").append(statements.length).append(" 个\n");
            return successCount > 0;
            
        } catch (Exception e) {
            logBuilder.append("SQL执行过程中发生异常: ").append(e.getMessage()).append("\n");
            logger.error("Error executing SQL script", e);
            return false;
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (Exception e) {
                    logger.error("Error closing database connection", e);
                }
            }
        }
    }
} 